import { Context, Next } from 'koa'
import { ServiceNo } from './ServiceNo'
import log from '../config/logger'

export {
  Context, Next
}

export interface Service {
  readonly serviceNo: string
  service(serviceNo: string, ctx: Context): any
}

export interface ServiceInterface {
  readonly methodNo: string
  readonly method: Function
  readonly methodDesc?: string
}

export abstract class BaseService implements Service {
  constructor (serviceNo: ServiceNo) {
    this.serviceNo = serviceNo.toString()
    this.methodMap = new Map<string, ServiceInterface>()
    this.init()
  }

  abstract init(): any
  methodMap: Map<string, ServiceInterface>
  serviceNo: string
  // eslint-disable-next-line no-undef
  [key: string]: any

  addServiceInterface (si: ServiceInterface) {
    this.methodMap.set(si.methodNo, si)
    return this
  }

  async service (serviceNo: string, ctx: Context) {
    const i = serviceNo.indexOf(this.serviceNo)
    const methodNo = serviceNo.substr(i + this.serviceNo.length)
    const serviceInterface = this.methodMap.get(methodNo)
    if (!serviceInterface) {
      this.throwMethodNotExist(serviceNo, ctx)
    }
    const serviceMethod = serviceInterface.method
    if (serviceMethod instanceof Function) {
      const serviceMethodName = serviceMethod.name
      const reqBody = ctx.request.body
      log.info(`invoking method ${serviceMethodName} with parameter: ${reqBody ? JSON.stringify(reqBody) : '{}'}`)
      await serviceMethod.call(this, ctx, reqBody || {})
    } else {
      this.throwMethodNotExist(serviceNo, ctx)
    }
  }

  private throwMethodNotExist (serviceNo: string, ctx: Context) {
    log.info(`${serviceNo} does not exists`)
    ctx.throw(404, `service [${serviceNo}] does not exists`)
  }
}
